/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "Precompiled.h"
#include "globaldata.h"

#include "i_system.h"
#include "z_zone.h"
#include "p_local.h"

// State.
#include "doomstat.h"
#include "r_state.h"



// Pads ::g->save_p to a 4-byte boundary
//  so that the load/save works on SGI&Gecko.



//
// P_ArchivePlayers
//
void P_ArchivePlayers( void )
{
	int		i;
	int		j;
	player_t*	dest;

	for( i = 0 ; i < MAXPLAYERS ; i++ )
	{
		if( !::g->playeringame[i] )
		{
			continue;
		}

		PADSAVEP();

		dest = ( player_t* )::g->save_p;
		memcpy( dest, &::g->players[i], sizeof( player_t ) );
		::g->save_p += sizeof( player_t );
		for( j = 0 ; j < NUMPSPRITES ; j++ )
		{
			if( dest->psprites[j].state )
			{
				dest->psprites[j].state
					= ( state_t* )( dest->psprites[j].state -::g->states );
			}
		}
	}
}



//
// P_UnArchivePlayers
//
void P_UnArchivePlayers( void )
{
	int		i;
	int		j;

	for( i = 0 ; i < MAXPLAYERS ; i++ )
	{
		if( !::g->playeringame[i] )
		{
			continue;
		}

		PADSAVEP();

		memcpy( &::g->players[i], ::g->save_p, sizeof( player_t ) );
		::g->save_p += sizeof( player_t );

		// will be set when unarc thinker
		::g->players[i].mo = NULL;
		::g->players[i].message = NULL;
		::g->players[i].attacker = NULL;

		for( j = 0 ; j < NUMPSPRITES ; j++ )
		{
			if( ::g->players[i]. psprites[j].state )
			{
				::g->players[i]. psprites[j].state
					= &::g->states[( intptr_t )::g->players[i].psprites[j].state ];
			}
		}
	}
}


//
// P_ArchiveWorld
//
void P_ArchiveWorld( void )
{
	int			i;
	int			j;
	sector_t*		sec;
	line_t*		li;
	side_t*		si;
	short*		put;

	put = ( short* )::g->save_p;

	// do ::g->sectors
	for( i = 0, sec = ::g->sectors ; i < ::g->numsectors ; i++, sec++ )
	{
		*put++ = sec->floorheight >> FRACBITS;
		*put++ = sec->ceilingheight >> FRACBITS;
		*put++ = sec->floorpic;
		*put++ = sec->ceilingpic;
		*put++ = sec->lightlevel;
		*put++ = sec->special;		// needed?
		*put++ = sec->tag;		// needed?
	}


	// do ::g->lines
	for( i = 0, li = ::g->lines ; i < ::g->numlines ; i++, li++ )
	{
		*put++ = li->flags;
		*put++ = li->special;
		*put++ = li->tag;
		for( j = 0 ; j < 2 ; j++ )
		{
			if( li->sidenum[j] == -1 )
			{
				continue;
			}

			si = &::g->sides[li->sidenum[j]];

			*put++ = si->textureoffset >> FRACBITS;
			*put++ = si->rowoffset >> FRACBITS;
			*put++ = si->toptexture;
			*put++ = si->bottomtexture;
			*put++ = si->midtexture;
		}
	}

	// Doom 2 level 30 requires some global pointers, wheee!
	*put++ = ::g->braintargeton;
	*put++ = ::g->easy;

	::g->save_p = ( byte* )put;
}



//
// P_UnArchiveWorld
//
void P_UnArchiveWorld( void )
{
	int			i;
	int			j;
	sector_t*	sec;
	line_t*		li;
	side_t*		si;
	short*		get;

	get = ( short* )::g->save_p;

	// do ::g->sectors
	for( i = 0, sec = ::g->sectors ; i < ::g->numsectors ; i++, sec++ )
	{
		sec->floorheight = *get++ << FRACBITS;
		sec->ceilingheight = *get++ << FRACBITS;
		sec->floorpic = *get++;
		sec->ceilingpic = *get++;
		sec->lightlevel = *get++;
		sec->special = *get++;		// needed?
		sec->tag = *get++;		// needed?
		sec->specialdata = 0;
		sec->soundtarget = 0;
	}

	// do ::g->lines
	for( i = 0, li = ::g->lines ; i < ::g->numlines ; i++, li++ )
	{
		li->flags = *get++;
		li->special = *get++;
		li->tag = *get++;
		for( j = 0 ; j < 2 ; j++ )
		{
			if( li->sidenum[j] == -1 )
			{
				continue;
			}
			si = &::g->sides[li->sidenum[j]];
			si->textureoffset = *get++ << FRACBITS;
			si->rowoffset = *get++ << FRACBITS;
			si->toptexture = *get++;
			si->bottomtexture = *get++;
			si->midtexture = *get++;
		}
	}

	// Doom 2 level 30 requires some global pointers, wheee!
	::g->braintargeton = *get++;
	::g->easy = *get++;

	::g->save_p = ( byte* )get;
}





//
// Thinkers
//

int GetMOIndex( mobj_t* findme )
{
	thinker_t*	th;
	mobj_t*		mobj;
	int			index = 0;

	for( th = ::g->thinkercap.next ; th != &::g->thinkercap ; th = th->next )
	{
		if( th->function.acp1 == ( actionf_p1 )P_MobjThinker )
		{
			index++;
			mobj = ( mobj_t* )th;

			if( mobj == findme )
			{
				return index;
			}
		}
	}

	return 0;
}

mobj_t* GetMO( int index )
{
	thinker_t*	th;
	int			testindex = 0;

	if( !index )
	{
		return NULL;
	}

	for( th = ::g->thinkercap.next ; th != &::g->thinkercap ; th = th->next )
	{
		if( th->function.acp1 == ( actionf_p1 )P_MobjThinker )
		{
			testindex++;

			if( testindex == index )
			{
				return ( mobj_t* )th;
			}
		}
	}

	return NULL;
}

//
// P_ArchiveThinkers
//
void P_ArchiveThinkers( void )
{
	thinker_t*		th;
	mobj_t*			mobj;
	ceiling_t*		ceiling;
	vldoor_t*		door;
	floormove_t*	floor;
	plat_t*			plat;
	fireflicker_t*	fire;
	lightflash_t*	flash;
	strobe_t*		strobe;
	glow_t*			glow;

	int i;

	// save off the current thinkers
	//I_Printf( "Savegame on leveltime %d\n====================\n", ::g->leveltime );

	for( th = ::g->thinkercap.next ; th != &::g->thinkercap ; th = th->next )
	{
		//mobj_t*	test = (mobj_t*)th;
		//I_Printf( "%3d: %x == function\n", index++, th->function.acp1 );

		if( th->function.acp1 == ( actionf_p1 )P_MobjThinker )
		{
			*::g->save_p++ = tc_mobj;
			PADSAVEP();

			mobj = ( mobj_t* )::g->save_p;
			memcpy( mobj, th, sizeof( *mobj ) );
			::g->save_p += sizeof( *mobj );
			mobj->state = ( state_t* )( mobj->state - ::g->states );

			if( mobj->player )
			{
				mobj->player = ( player_t* )( ( mobj->player -::g->players ) + 1 );
			}

			// Save out 'target'
			int moIndex = GetMOIndex( mobj->target );
			*::g->save_p++ = moIndex >> 8;
			*::g->save_p++ = moIndex;

			// Save out 'tracer'
			moIndex = GetMOIndex( mobj->tracer );
			*::g->save_p++ = moIndex >> 8;
			*::g->save_p++ = moIndex;

			moIndex = GetMOIndex( mobj->snext );
			*::g->save_p++ = moIndex >> 8;
			*::g->save_p++ = moIndex;

			moIndex = GetMOIndex( mobj->sprev );
			*::g->save_p++ = moIndex >> 8;
			*::g->save_p++ = moIndex;

			// Is this the head of a sector list?
			if( mobj->subsector->sector->thinglist == ( mobj_t* )th )
			{
				*::g->save_p++ = 1;
			}
			else
			{
				*::g->save_p++ = 0;
			}

			moIndex = GetMOIndex( mobj->bnext );
			*::g->save_p++ = moIndex >> 8;
			*::g->save_p++ = moIndex;

			moIndex = GetMOIndex( mobj->bprev );
			*::g->save_p++ = moIndex >> 8;
			*::g->save_p++ = moIndex;

			// Is this the head of a block list?
			int	blockx = ( mobj->x - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
			int	blocky = ( mobj->y - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
			if( blockx >= 0 && blockx < ::g->bmapwidth && blocky >= 0 && blocky < ::g->bmapheight
					&& ( mobj_t* )th == ::g->blocklinks[blocky*::g->bmapwidth + blockx] )
			{

				*::g->save_p++ = 1;
			}
			else
			{
				*::g->save_p++ = 0;
			}
			continue;
		}

		if( th->function.acv == ( actionf_v )NULL )
		{
			for( i = 0; i < MAXCEILINGS; i++ )
				if( ::g->activeceilings[i] == ( ceiling_t* )th )
				{
					break;
				}

			if( i < MAXCEILINGS )
			{
				*::g->save_p++ = tc_ceiling;
				PADSAVEP();
				ceiling = ( ceiling_t* )::g->save_p;
				memcpy( ceiling, th, sizeof( *ceiling ) );
				::g->save_p += sizeof( *ceiling );
				ceiling->sector = ( sector_t* )( ceiling->sector - ::g->sectors );
			}
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_MoveCeiling )
		{
			*::g->save_p++ = tc_ceiling;
			PADSAVEP();
			ceiling = ( ceiling_t* )::g->save_p;
			memcpy( ceiling, th, sizeof( *ceiling ) );
			::g->save_p += sizeof( *ceiling );
			ceiling->sector = ( sector_t* )( ceiling->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_VerticalDoor )
		{
			*::g->save_p++ = tc_door;
			PADSAVEP();
			door = ( vldoor_t* )::g->save_p;
			memcpy( door, th, sizeof( *door ) );
			::g->save_p += sizeof( *door );
			door->sector = ( sector_t* )( door->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_MoveFloor )
		{
			*::g->save_p++ = tc_floor;
			PADSAVEP();
			floor = ( floormove_t* )::g->save_p;
			memcpy( floor, th, sizeof( *floor ) );
			::g->save_p += sizeof( *floor );
			floor->sector = ( sector_t* )( floor->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_PlatRaise )
		{
			*::g->save_p++ = tc_plat;
			PADSAVEP();
			plat = ( plat_t* )::g->save_p;
			memcpy( plat, th, sizeof( *plat ) );
			::g->save_p += sizeof( *plat );
			plat->sector = ( sector_t* )( plat->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_FireFlicker )
		{
			*::g->save_p++ = tc_fire;
			PADSAVEP();
			fire = ( fireflicker_t* )::g->save_p;
			memcpy( fire, th, sizeof( *fire ) );
			::g->save_p += sizeof( *fire );
			fire->sector = ( sector_t* )( fire->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_LightFlash )
		{
			*::g->save_p++ = tc_flash;
			PADSAVEP();
			flash = ( lightflash_t* )::g->save_p;
			memcpy( flash, th, sizeof( *flash ) );
			::g->save_p += sizeof( *flash );
			flash->sector = ( sector_t* )( flash->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_StrobeFlash )
		{
			*::g->save_p++ = tc_strobe;
			PADSAVEP();
			strobe = ( strobe_t* )::g->save_p;
			memcpy( strobe, th, sizeof( *strobe ) );
			::g->save_p += sizeof( *strobe );
			strobe->sector = ( sector_t* )( strobe->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_Glow )
		{
			*::g->save_p++ = tc_glow;
			PADSAVEP();
			glow = ( glow_t* )::g->save_p;
			memcpy( glow, th, sizeof( *glow ) );
			::g->save_p += sizeof( *glow );
			glow->sector = ( sector_t* )( glow->sector - ::g->sectors );
			continue;
		}
	}

	// add a terminating marker
	*::g->save_p++ = tc_end;

	sector_t* sec;
	short* put = ( short* )::g->save_p;
	for( i = 0, sec = ::g->sectors ; i < ::g->numsectors ; i++, sec++ )
	{
		*put++ = ( short )GetMOIndex( sec->soundtarget );
	}

	::g->save_p = ( byte* )put;

	// add a terminating marker
	*::g->save_p++ = tc_end;
}



//
// P_UnArchiveThinkers
//
void P_UnArchiveThinkers( void )
{
	byte			tclass;
	thinker_t*		currentthinker;
	thinker_t*		next;
	mobj_t*			mobj;
	ceiling_t*		ceiling;
	vldoor_t*		door;
	floormove_t*	floor;
	plat_t*			plat;
	fireflicker_t*	fire;
	lightflash_t*	flash;
	strobe_t*		strobe;
	glow_t*			glow;

	thinker_t*	th;

	int count = 0;
	sector_t* ss = NULL;

	int			mo_index = 0;
	int			mo_targets[1024];
	int			mo_tracers[1024];
	int			mo_snext[1024];
	int			mo_sprev[1024];
	bool		mo_shead[1024];
	int			mo_bnext[1024];
	int			mo_bprev[1024];
	bool		mo_bhead[1024];

	// remove all the current thinkers
	currentthinker = ::g->thinkercap.next;
	while( currentthinker != &::g->thinkercap )
	{
		next = currentthinker->next;

		if( currentthinker->function.acp1 == ( actionf_p1 )P_MobjThinker )
		{
			P_RemoveMobj( ( mobj_t* )currentthinker );
		}
		else
		{
			Z_Free( currentthinker );
		}

		currentthinker = next;
	}

	P_InitThinkers();

	// read in saved thinkers
	while( 1 )
	{
		tclass = *::g->save_p++;
		switch( tclass )
		{
			case tc_end:

				// clear sector thing lists
				ss = ::g->sectors;
				for( int i = 0 ; i < ::g->numsectors ; i++, ss++ )
				{
					ss->thinglist = NULL;
				}

				// clear blockmap thing lists
				count = sizeof( *::g->blocklinks ) * ::g->bmapwidth * ::g->bmapheight;
				memset( ::g->blocklinks, 0, count );

				// Doom 2 level 30 requires some global pointers, wheee!
				::g->numbraintargets = 0;

				// fixup mobj_t pointers now that all thinkers have been restored
				mo_index = 0;
				for( th = ::g->thinkercap.next ; th != &::g->thinkercap ; th = th->next )
				{
					if( th->function.acp1 == ( actionf_p1 )P_MobjThinker )
					{
						mobj = ( mobj_t* )th;

						mobj->target = GetMO( mo_targets[mo_index] );
						mobj->tracer = GetMO( mo_tracers[mo_index] );

						mobj->snext = GetMO( mo_snext[mo_index] );
						mobj->sprev = GetMO( mo_sprev[mo_index] );

						if( mo_shead[mo_index] )
						{
							mobj->subsector->sector->thinglist = mobj;
						}

						mobj->bnext = GetMO( mo_bnext[mo_index] );
						mobj->bprev = GetMO( mo_bprev[mo_index] );

						if( mo_bhead[mo_index] )
						{
							// Is this the head of a block list?
							int	blockx = ( mobj->x - ::g->bmaporgx ) >> MAPBLOCKSHIFT;
							int	blocky = ( mobj->y - ::g->bmaporgy ) >> MAPBLOCKSHIFT;
							if( blockx >= 0 && blockx < ::g->bmapwidth && blocky >= 0 && blocky < ::g->bmapheight )
							{
								::g->blocklinks[blocky*::g->bmapwidth + blockx] = mobj;
							}
						}

						// Doom 2 level 30 requires some global pointers, wheee!
						if( mobj->type == MT_BOSSTARGET )
						{
							::g->braintargets[::g->numbraintargets] = mobj;
							::g->numbraintargets++;
						}

						mo_index++;
					}
				}

				int i;
				sector_t*	sec;
				short*	get;

				get = ( short* )::g->save_p;
				for( i = 0, sec = ::g->sectors ; i < ::g->numsectors ; i++, sec++ )
				{
					sec->soundtarget = GetMO( *get++ );
				}
				::g->save_p = ( byte* )get;

				tclass = *::g->save_p++;
				if( tclass != tc_end )
				{
					I_Error( "Savegame error after loading sector soundtargets." );
				}

				// print the current thinkers
				//I_Printf( "Loadgame on leveltime %d\n====================\n", ::g->leveltime );
				for( th = ::g->thinkercap.next ; th != &::g->thinkercap ; th = th->next )
				{
					//mobj_t*	test = (mobj_t*)th;
					//I_Printf( "%3d: %x == function\n", index++, th->function.acp1 );
				}

				return; 	// end of list

			case tc_mobj:
				PADSAVEP();
				mobj = ( mobj_t* )DoomLib::Z_Malloc( sizeof( *mobj ), PU_LEVEL, NULL );
				memcpy( mobj, ::g->save_p, sizeof( *mobj ) );
				::g->save_p += sizeof( *mobj );
				mobj->state = &::g->states[( intptr_t )mobj->state];

				mobj->target = NULL;
				mobj->tracer = NULL;

				if( mobj->player )
				{
					mobj->player = &::g->players[( intptr_t )mobj->player - 1];
					mobj->player->mo = mobj;
				}

				P_SetThingPosition( mobj );

				mobj->info = &mobjinfo[mobj->type];
				mobj->floorz = mobj->subsector->sector->floorheight;
				mobj->ceilingz = mobj->subsector->sector->ceilingheight;
				mobj->thinker.function.acp1 = ( actionf_p1 )P_MobjThinker;

				// Read in 'target' and store for fixup
				int a, b, foundIndex;
				a = *::g->save_p++;
				b = *::g->save_p++;
				foundIndex = ( a << 8 ) + b;
				mo_targets[mo_index] = foundIndex;

				// Read in 'tracer' and store for fixup
				a = *::g->save_p++;
				b = *::g->save_p++;
				foundIndex = ( a << 8 ) + b;
				mo_tracers[mo_index] = foundIndex;

				// Read in 'snext' and store for fixup
				a = *::g->save_p++;
				b = *::g->save_p++;
				foundIndex = ( a << 8 ) + b;
				mo_snext[mo_index] = foundIndex;

				// Read in 'sprev' and store for fixup
				a = *::g->save_p++;
				b = *::g->save_p++;
				foundIndex = ( a << 8 ) + b;
				mo_sprev[mo_index] = foundIndex;

				foundIndex = *::g->save_p++;
				mo_shead[mo_index] = foundIndex == 1;

				// Read in 'bnext' and store for fixup
				a = *::g->save_p++;
				b = *::g->save_p++;
				foundIndex = ( a << 8 ) + b;
				mo_bnext[mo_index] = foundIndex;

				// Read in 'bprev' and store for fixup
				a = *::g->save_p++;
				b = *::g->save_p++;
				foundIndex = ( a << 8 ) + b;
				mo_bprev[mo_index] = foundIndex;

				foundIndex = *::g->save_p++;
				mo_bhead[mo_index] = foundIndex == 1;

				mo_index++;

				P_AddThinker( &mobj->thinker );
				break;

			case tc_ceiling:
				PADSAVEP();
				ceiling = ( ceiling_t* )DoomLib::Z_Malloc( sizeof( *ceiling ), PU_LEVEL, NULL );
				memcpy( ceiling, ::g->save_p, sizeof( *ceiling ) );
				::g->save_p += sizeof( *ceiling );
				ceiling->sector = &::g->sectors[( intptr_t )ceiling->sector];
				ceiling->sector->specialdata = ceiling;

				if( ceiling->thinker.function.acp1 )
				{
					ceiling->thinker.function.acp1 = ( actionf_p1 )T_MoveCeiling;
				}

				P_AddThinker( &ceiling->thinker );
				P_AddActiveCeiling( ceiling );
				break;

			case tc_door:
				PADSAVEP();
				door = ( vldoor_t* )DoomLib::Z_Malloc( sizeof( *door ), PU_LEVEL, NULL );
				memcpy( door, ::g->save_p, sizeof( *door ) );
				::g->save_p += sizeof( *door );
				door->sector = &::g->sectors[( intptr_t )door->sector];
				door->sector->specialdata = door;
				door->thinker.function.acp1 = ( actionf_p1 )T_VerticalDoor;
				P_AddThinker( &door->thinker );
				break;

			case tc_floor:
				PADSAVEP();
				floor = ( floormove_t* )DoomLib::Z_Malloc( sizeof( *floor ), PU_LEVEL, NULL );
				memcpy( floor, ::g->save_p, sizeof( *floor ) );
				::g->save_p += sizeof( *floor );
				floor->sector = &::g->sectors[( intptr_t )floor->sector];
				floor->sector->specialdata = floor;
				floor->thinker.function.acp1 = ( actionf_p1 )T_MoveFloor;
				P_AddThinker( &floor->thinker );
				break;

			case tc_plat:
				PADSAVEP();
				plat = ( plat_t* )DoomLib::Z_Malloc( sizeof( *plat ), PU_LEVEL, NULL );
				memcpy( plat, ::g->save_p, sizeof( *plat ) );
				::g->save_p += sizeof( *plat );
				plat->sector = &::g->sectors[( intptr_t )plat->sector];
				plat->sector->specialdata = plat;

				if( plat->thinker.function.acp1 )
				{
					plat->thinker.function.acp1 = ( actionf_p1 )T_PlatRaise;
				}

				P_AddThinker( &plat->thinker );
				P_AddActivePlat( plat );
				break;

			case tc_fire:
				PADSAVEP();
				fire = ( fireflicker_t* )DoomLib::Z_Malloc( sizeof( *fire ), PU_LEVEL, NULL );
				memcpy( fire, ::g->save_p, sizeof( *fire ) );
				::g->save_p += sizeof( *fire );
				fire->sector = &::g->sectors[( intptr_t )fire->sector];
				fire->thinker.function.acp1 = ( actionf_p1 )T_FireFlicker;
				P_AddThinker( &fire->thinker );
				break;

			case tc_flash:
				PADSAVEP();
				flash = ( lightflash_t* )DoomLib::Z_Malloc( sizeof( *flash ), PU_LEVEL, NULL );
				memcpy( flash, ::g->save_p, sizeof( *flash ) );
				::g->save_p += sizeof( *flash );
				flash->sector = &::g->sectors[( intptr_t )flash->sector];
				flash->thinker.function.acp1 = ( actionf_p1 )T_LightFlash;
				P_AddThinker( &flash->thinker );
				break;

			case tc_strobe:
				PADSAVEP();
				strobe = ( strobe_t* )DoomLib::Z_Malloc( sizeof( *strobe ), PU_LEVEL, NULL );
				memcpy( strobe, ::g->save_p, sizeof( *strobe ) );
				::g->save_p += sizeof( *strobe );
				strobe->sector = &::g->sectors[( intptr_t )strobe->sector];
				strobe->thinker.function.acp1 = ( actionf_p1 )T_StrobeFlash;
				P_AddThinker( &strobe->thinker );
				break;

			case tc_glow:
				PADSAVEP();
				glow = ( glow_t* )DoomLib::Z_Malloc( sizeof( *glow ), PU_LEVEL, NULL );
				memcpy( glow, ::g->save_p, sizeof( *glow ) );
				::g->save_p += sizeof( *glow );
				glow->sector = &::g->sectors[( intptr_t )glow->sector];
				glow->thinker.function.acp1 = ( actionf_p1 )T_Glow;
				P_AddThinker( &glow->thinker );
				break;

			default:
				I_Error( "Unknown tclass %i in savegame", tclass );
		}
	}
}


//
// P_ArchiveSpecials
//



//
// Things to handle:
//
// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list
// T_VerticalDoor, (vldoor_t: sector_t * swizzle),
// T_MoveFloor, (floormove_t: sector_t * swizzle),
// T_LightFlash, (lightflash_t: sector_t * swizzle),
// T_StrobeFlash, (strobe_t: sector_t *),
// T_Glow, (glow_t: sector_t *),
// T_PlatRaise, (plat_t: sector_t *), - active list
//
void P_ArchiveSpecials( void )
{
	thinker_t*		th;
	ceiling_t*		ceiling;
	vldoor_t*		door;
	floormove_t*	floor;
	plat_t*		plat;
	lightflash_t*	flash;
	strobe_t*		strobe;
	glow_t*		glow;
	int			i;

	// save off the current thinkers
	for( th = ::g->thinkercap.next ; th != &::g->thinkercap ; th = th->next )
	{
		if( th->function.acv == ( actionf_v )NULL )
		{
			for( i = 0; i < MAXCEILINGS; i++ )
				if( ::g->activeceilings[i] == ( ceiling_t* )th )
				{
					break;
				}

			if( i < MAXCEILINGS )
			{
				*::g->save_p++ = tc_ceiling;
				PADSAVEP();
				ceiling = ( ceiling_t* )::g->save_p;
				memcpy( ceiling, th, sizeof( *ceiling ) );
				::g->save_p += sizeof( *ceiling );
				ceiling->sector = ( sector_t* )( ceiling->sector - ::g->sectors );
			}
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_MoveCeiling )
		{
			*::g->save_p++ = tc_ceiling;
			PADSAVEP();
			ceiling = ( ceiling_t* )::g->save_p;
			memcpy( ceiling, th, sizeof( *ceiling ) );
			::g->save_p += sizeof( *ceiling );
			ceiling->sector = ( sector_t* )( ceiling->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_VerticalDoor )
		{
			*::g->save_p++ = tc_door;
			PADSAVEP();
			door = ( vldoor_t* )::g->save_p;
			memcpy( door, th, sizeof( *door ) );
			::g->save_p += sizeof( *door );
			door->sector = ( sector_t* )( door->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_MoveFloor )
		{
			*::g->save_p++ = tc_floor;
			PADSAVEP();
			floor = ( floormove_t* )::g->save_p;
			memcpy( floor, th, sizeof( *floor ) );
			::g->save_p += sizeof( *floor );
			floor->sector = ( sector_t* )( floor->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_PlatRaise )
		{
			*::g->save_p++ = tc_plat;
			PADSAVEP();
			plat = ( plat_t* )::g->save_p;
			memcpy( plat, th, sizeof( *plat ) );
			::g->save_p += sizeof( *plat );
			plat->sector = ( sector_t* )( plat->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_LightFlash )
		{
			*::g->save_p++ = tc_flash;
			PADSAVEP();
			flash = ( lightflash_t* )::g->save_p;
			memcpy( flash, th, sizeof( *flash ) );
			::g->save_p += sizeof( *flash );
			flash->sector = ( sector_t* )( flash->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_StrobeFlash )
		{
			*::g->save_p++ = tc_strobe;
			PADSAVEP();
			strobe = ( strobe_t* )::g->save_p;
			memcpy( strobe, th, sizeof( *strobe ) );
			::g->save_p += sizeof( *strobe );
			strobe->sector = ( sector_t* )( strobe->sector - ::g->sectors );
			continue;
		}

		if( th->function.acp1 == ( actionf_p1 )T_Glow )
		{
			*::g->save_p++ = tc_glow;
			PADSAVEP();
			glow = ( glow_t* )::g->save_p;
			memcpy( glow, th, sizeof( *glow ) );
			::g->save_p += sizeof( *glow );
			glow->sector = ( sector_t* )( glow->sector - ::g->sectors );
			continue;
		}
	}

	// add a terminating marker
	*::g->save_p++ = tc_endspecials;

}


//
// P_UnArchiveSpecials
//
void P_UnArchiveSpecials( void )
{
	byte		tclass;
	ceiling_t*		ceiling;
	vldoor_t*		door;
	floormove_t*	floor;
	plat_t*		plat;
	lightflash_t*	flash;
	strobe_t*		strobe;
	glow_t*		glow;

	// read in saved thinkers
	while( 1 )
	{
		tclass = *::g->save_p++;
		switch( tclass )
		{
			case tc_endspecials:
				return;	// end of list

			case tc_ceiling:
				PADSAVEP();
				ceiling = ( ceiling_t* )DoomLib::Z_Malloc( sizeof( *ceiling ), PU_LEVEL, NULL );
				memcpy( ceiling, ::g->save_p, sizeof( *ceiling ) );
				::g->save_p += sizeof( *ceiling );
				ceiling->sector = &::g->sectors[( intptr_t )ceiling->sector];
				ceiling->sector->specialdata = ceiling;

				if( ceiling->thinker.function.acp1 )
				{
					ceiling->thinker.function.acp1 = ( actionf_p1 )T_MoveCeiling;
				}

				P_AddThinker( &ceiling->thinker );
				P_AddActiveCeiling( ceiling );
				break;

			case tc_door:
				PADSAVEP();
				door = ( vldoor_t* )DoomLib::Z_Malloc( sizeof( *door ), PU_LEVEL, NULL );
				memcpy( door, ::g->save_p, sizeof( *door ) );
				::g->save_p += sizeof( *door );
				door->sector = &::g->sectors[( intptr_t )door->sector];
				door->sector->specialdata = door;
				door->thinker.function.acp1 = ( actionf_p1 )T_VerticalDoor;
				P_AddThinker( &door->thinker );
				break;

			case tc_floor:
				PADSAVEP();
				floor = ( floormove_t* )DoomLib::Z_Malloc( sizeof( *floor ), PU_LEVEL, NULL );
				memcpy( floor, ::g->save_p, sizeof( *floor ) );
				::g->save_p += sizeof( *floor );
				floor->sector = &::g->sectors[( intptr_t )floor->sector];
				floor->sector->specialdata = floor;
				floor->thinker.function.acp1 = ( actionf_p1 )T_MoveFloor;
				P_AddThinker( &floor->thinker );
				break;

			case tc_plat:
				PADSAVEP();
				plat = ( plat_t* )DoomLib::Z_Malloc( sizeof( *plat ), PU_LEVEL, NULL );
				memcpy( plat, ::g->save_p, sizeof( *plat ) );
				::g->save_p += sizeof( *plat );
				plat->sector = &::g->sectors[( intptr_t )plat->sector];
				plat->sector->specialdata = plat;

				if( plat->thinker.function.acp1 )
				{
					plat->thinker.function.acp1 = ( actionf_p1 )T_PlatRaise;
				}

				P_AddThinker( &plat->thinker );
				P_AddActivePlat( plat );
				break;

			case tc_flash:
				PADSAVEP();
				flash = ( lightflash_t* )DoomLib::Z_Malloc( sizeof( *flash ), PU_LEVEL, NULL );
				memcpy( flash, ::g->save_p, sizeof( *flash ) );
				::g->save_p += sizeof( *flash );
				flash->sector = &::g->sectors[( intptr_t )flash->sector];
				flash->thinker.function.acp1 = ( actionf_p1 )T_LightFlash;
				P_AddThinker( &flash->thinker );
				break;

			case tc_strobe:
				PADSAVEP();
				strobe = ( strobe_t* )DoomLib::Z_Malloc( sizeof( *strobe ), PU_LEVEL, NULL );
				memcpy( strobe, ::g->save_p, sizeof( *strobe ) );
				::g->save_p += sizeof( *strobe );
				strobe->sector = &::g->sectors[( intptr_t )strobe->sector];
				strobe->thinker.function.acp1 = ( actionf_p1 )T_StrobeFlash;
				P_AddThinker( &strobe->thinker );
				break;

			case tc_glow:
				PADSAVEP();
				glow = ( glow_t* )DoomLib::Z_Malloc( sizeof( *glow ), PU_LEVEL, NULL );
				memcpy( glow, ::g->save_p, sizeof( *glow ) );
				::g->save_p += sizeof( *glow );
				glow->sector = &::g->sectors[( intptr_t )glow->sector];
				glow->thinker.function.acp1 = ( actionf_p1 )T_Glow;
				P_AddThinker( &glow->thinker );
				break;

			default:
				I_Error( "P_UnarchiveSpecials:Unknown tclass %i "
						 "in savegame", tclass );
		}

	}

}


